From fcf9e70d54bd737d2dea848d25314120f37db503 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sun, 18 Jan 2026 02:58:27 +0000
Subject: [WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC

---
 apps/mobile/app/task/[id].tsx | 259 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 259 insertions(+)
 create mode 100644 apps/mobile/app/task/[id].tsx

(limited to 'apps/mobile/app/task/[id].tsx')

diff --git a/apps/mobile/app/task/[id].tsx b/apps/mobile/app/task/[id].tsx
new file mode 100644
index 0000000..121063a
--- /dev/null
+++ b/apps/mobile/app/task/[id].tsx
@@ -0,0 +1,259 @@
+import React from 'react';
+import {
+  View,
+  Text,
+  StyleSheet,
+  ScrollView,
+  useColorScheme,
+  ActivityIndicator,
+} from 'react-native';
+import { useLocalSearchParams, Stack } from 'expo-router';
+import { Colors } from '../../constants/Colors';
+import { useTask } from '../../hooks/useTasks';
+import { TaskStatusBadge } from '../../components/TaskStatusBadge';
+import { EmptyState } from '../../components/EmptyState';
+
+export default function TaskDetailScreen() {
+  const colorScheme = useColorScheme() ?? 'light';
+  const colors = Colors[colorScheme];
+  const { id } = useLocalSearchParams<{ id: string }>();
+
+  const { data: task, isLoading, isError } = useTask(id);
+
+  if (isLoading) {
+    return (
+      <>
+        <Stack.Screen options={{ title: 'Loading...' }} />
+        <View style={[styles.container, styles.centered, { backgroundColor: colors.background }]}>
+          <ActivityIndicator size="large" color={colors.tint} />
+        </View>
+      </>
+    );
+  }
+
+  if (isError || !task) {
+    return (
+      <>
+        <Stack.Screen options={{ title: 'Error' }} />
+        <View style={[styles.container, { backgroundColor: colors.background }]}>
+          <EmptyState
+            icon="alert-circle-outline"
+            title="Failed to load task"
+            message="The task could not be found or an error occurred"
+          />
+        </View>
+      </>
+    );
+  }
+
+  return (
+    <>
+      <Stack.Screen options={{ title: task.name }} />
+      <ScrollView
+        style={[styles.container, { backgroundColor: colors.background }]}
+        contentContainerStyle={styles.content}
+      >
+        {/* Header */}
+        <View style={[styles.header, { backgroundColor: colors.card }]}>
+          <View style={styles.headerTop}>
+            <TaskStatusBadge status={task.status} showLabel size="large" />
+          </View>
+          <Text style={[styles.taskName, { color: colors.text }]}>
+            {task.name}
+          </Text>
+          {task.description && (
+            <Text style={[styles.description, { color: colors.secondaryText }]}>
+              {task.description}
+            </Text>
+          )}
+        </View>
+
+        {/* Progress Summary */}
+        {task.progressSummary && (
+          <View style={[styles.section, { backgroundColor: colors.card }]}>
+            <Text style={[styles.sectionTitle, { color: colors.text }]}>
+              Progress
+            </Text>
+            <Text style={[styles.progressText, { color: colors.secondaryText }]}>
+              {task.progressSummary}
+            </Text>
+          </View>
+        )}
+
+        {/* Task Info */}
+        <View style={[styles.section, { backgroundColor: colors.card }]}>
+          <Text style={[styles.sectionTitle, { color: colors.text }]}>
+            Details
+          </Text>
+
+          <View style={styles.infoRow}>
+            <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+              Created
+            </Text>
+            <Text style={[styles.infoValue, { color: colors.text }]}>
+              {new Date(task.createdAt).toLocaleString()}
+            </Text>
+          </View>
+
+          {task.startedAt && (
+            <View style={styles.infoRow}>
+              <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+                Started
+              </Text>
+              <Text style={[styles.infoValue, { color: colors.text }]}>
+                {new Date(task.startedAt).toLocaleString()}
+              </Text>
+            </View>
+          )}
+
+          {task.completedAt && (
+            <View style={styles.infoRow}>
+              <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+                Completed
+              </Text>
+              <Text style={[styles.infoValue, { color: colors.text }]}>
+                {new Date(task.completedAt).toLocaleString()}
+              </Text>
+            </View>
+          )}
+
+          {task.repositoryUrl && (
+            <View style={styles.infoRow}>
+              <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+                Repository
+              </Text>
+              <Text
+                style={[styles.infoValue, { color: colors.tint }]}
+                numberOfLines={1}
+              >
+                {task.repositoryUrl}
+              </Text>
+            </View>
+          )}
+        </View>
+
+        {/* Subtasks */}
+        {task.subtasks && task.subtasks.length > 0 && (
+          <View style={[styles.section, { backgroundColor: colors.card }]}>
+            <Text style={[styles.sectionTitle, { color: colors.text }]}>
+              Subtasks ({task.subtasks.length})
+            </Text>
+            {task.subtasks.map((subtask) => (
+              <View key={subtask.id} style={styles.subtaskRow}>
+                <TaskStatusBadge status={subtask.status} size="small" />
+                <Text
+                  style={[styles.subtaskName, { color: colors.text }]}
+                  numberOfLines={1}
+                >
+                  {subtask.name}
+                </Text>
+              </View>
+            ))}
+          </View>
+        )}
+
+        {/* Error message */}
+        {task.errorMessage && (
+          <View style={[styles.section, styles.errorSection]}>
+            <Text style={[styles.sectionTitle, { color: '#991b1b' }]}>
+              Error
+            </Text>
+            <Text style={styles.errorText}>{task.errorMessage}</Text>
+          </View>
+        )}
+
+        {/* Placeholder for future features */}
+        <View style={styles.placeholder}>
+          <Text style={[styles.placeholderText, { color: colors.secondaryText }]}>
+            Task output and controls will be added here
+          </Text>
+        </View>
+      </ScrollView>
+    </>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+  },
+  centered: {
+    justifyContent: 'center',
+    alignItems: 'center',
+  },
+  content: {
+    padding: 16,
+    gap: 16,
+  },
+  header: {
+    padding: 16,
+    borderRadius: 12,
+    gap: 8,
+  },
+  headerTop: {
+    flexDirection: 'row',
+    justifyContent: 'flex-start',
+  },
+  taskName: {
+    fontSize: 20,
+    fontWeight: '700',
+  },
+  description: {
+    fontSize: 14,
+    lineHeight: 20,
+  },
+  section: {
+    padding: 16,
+    borderRadius: 12,
+    gap: 12,
+  },
+  sectionTitle: {
+    fontSize: 16,
+    fontWeight: '600',
+  },
+  progressText: {
+    fontSize: 14,
+    lineHeight: 20,
+  },
+  infoRow: {
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+  },
+  infoLabel: {
+    fontSize: 14,
+  },
+  infoValue: {
+    fontSize: 14,
+    fontWeight: '500',
+    flex: 1,
+    textAlign: 'right',
+    marginLeft: 16,
+  },
+  subtaskRow: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 8,
+    paddingVertical: 4,
+  },
+  subtaskName: {
+    fontSize: 14,
+    flex: 1,
+  },
+  errorSection: {
+    backgroundColor: '#fee2e2',
+  },
+  errorText: {
+    fontSize: 14,
+    color: '#991b1b',
+    lineHeight: 20,
+  },
+  placeholder: {
+    padding: 32,
+    alignItems: 'center',
+  },
+  placeholderText: {
+    fontSize: 14,
+    textAlign: 'center',
+  },
+});
-- 
cgit v1.2.3

